package download;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import customization.Constants;

public class WebFunction 
{
	/** counter number of calls executed for all functions **/
	public static int counterCalls=0;
	
	/** function name and the name of the site **/
	public String functionName=null;
	public String site=null;
	public String signature=null; /** this is used for comparison **/
	
	/** informations to call the URL**/
	public String prefix=null;
	public String suffix=null;
	
	/** inputs and where to store the results **/
	private String dirWithResults=null;
	
	 /** this field will contain the error message of the last download page */
	public String lastDownloadErrorMessage;
	
	/** cache for calls: the input is the key in the map **/
	HashMap<String, PairValidityAndErrorMessage> historyOfCalls;

	/** valid inputs tested recently **/
	public ArrayList<String> validInputs=null;
	
	/****************************************/
	/** CONSTRUCTORS **/
	/****************************************/
	public WebFunction(String name, String site, String prefix, String suffix) throws Exception{
		String siteDir=Constants.projectDirectory+site;
		
		/** if the directory for the site does not exit, create it **/
		File f= new File(siteDir);
		if(!(f.exists() && f.isDirectory())){
			boolean success = (new File(siteDir)).mkdirs();
			if (!success) {
			    return;
			}
		}
		
		String filesDir=siteDir+"/"+name+"/";
		
		/** if the directory for the function is created, create it **/ 
		f= new File(filesDir);
		if(!(f.exists() && f.isDirectory())){
			boolean success = (new File(filesDir)).mkdirs();
			if (!success) {
			    return;
			}
		}
	
		this.dirWithResults=filesDir;
		
		this.prefix=prefix;
		this.suffix=suffix;
		this.functionName=name;
		this.site=site;
		this.signature=site+":"+name;
		
		/** load the history of the calls if any **/
		loadCache();
		
		/** print the history of calls **/
		System.out.println("The calls have been previously tested "+historyOfCalls.keySet());
	}

	
	/****************************************/
	/** IF THIS CLASS IS USED AS KEY IN A HASHMAP **/
	/****************************************/
	@Override
	public int hashCode(){
		return signature.hashCode();
	}
		
	@Override
	public boolean equals(Object o){
		if (!(o instanceof WebFunction)) {
			return false;
		}
		WebFunction of=(WebFunction)o;
		return signature.equals(of.signature);
	}
	
	/****************************************/
	/** GET PATHS **/
	/****************************************/
    public void executeCallsForInputList(String fileInputs) throws Exception{
 		executeCalls(readLineByLineTheInputs(fileInputs));
 	}
    
    public void executeCalls(Collection<String> inputs) throws Exception{
 		for(String input: inputs) {
 			executeCall(input);
 		}
 	}
    public void executeCallsAndSetValidInputs(Collection<String> inputs) throws Exception{
 		if(validInputs==null) validInputs=new ArrayList<String>();
    		for(String input: inputs) {
 			String file=executeCall(input);
 			if(file!=null) validInputs.add(input);
 		}
 	}
    
    /** For Mary: returns the filePath where the result of the call is stored  
     * returns null if problems downloading the file; 
     * the error message is stored in the field (see above) **/
    public String executeCall(String input) {
    			String filePath=getPathResultForCall(input);
    			File f= new File(filePath);
    			if(f.exists()) {
    				/** add the call to the cache **/
    				historyOfCalls.put(input, new PairValidityAndErrorMessage(Boolean.TRUE));
    				return filePath;
    			}
    			
    			/** if it's a previously known invalid call, return null **/ 
    			if(historyOfCalls.get(input)!=null && !historyOfCalls.get(input).validity) {
    				System.out.println("The input "+input+" has been already checked as being invalid ");
    				return null;
    			}
    			System.out.println("The input "+input+" does not appear in the history"); 
    			
    			String url=getURLForInput(input);
			filePath=getFileFromURL(input, url, filePath);
			try{
				Thread.sleep(1000);
			}
			catch (Exception e){}
			return filePath;
    }
    
    public String getPathResultForCall(String input){
		return dirWithResults+input.replaceAll("\\s+","+");
	}
	
    
    private String getURLForInput(String input){
		if(input==null | input.length()==0) return null;
		return prefix+input.replaceAll("\\s+","+")+suffix;
	}
    
  	public String getFileFromURL(String inputValue, String host, String filePath) 
      {
  		  if(Constants.offline) return null;
  		  counterCalls++;
  		  String newLine = System.getProperty("line.separator");
  		  BufferedReader in=null;
          Writer writer = null;
        
          System.out.println("Download file "+host);
          try
          {
              URL url = new URL(host);
              in = new BufferedReader(new InputStreamReader(url.openStream()));
             
              /** I remove empty lines until the first character **/
              String line;
              while ((line = in.readLine()) != null ) {
                  line=line.trim();
                  if(!line.equals("")) break;
              }
             
              /** create a string writer if JSON detected **/
              boolean isJSONData=false;
              if(line!=null){
            	  	if(line.startsWith("{")) {
            	  		System.out.println("JSON detected");
            	  		writer = new StringWriter();
            	  		isJSONData=true;
            	  	}else {
            	  		writer = new FileWriter(filePath);
            	  	}
            	  	writer.write(line+newLine);
              }
              
              /** write the rest of the input file **/
              while ((line = in.readLine()) != null) {
            	  	   writer.write(line+newLine);
              }
              writer.flush();
              
              /** if it's json data do the transformation **/
              if(isJSONData) JSONToXML.transformToXML(((StringWriter)writer).toString(), filePath);
              
              
              /** if the download is a success, add the input to the list of valid inputs for which a file is stored **/
              historyOfCalls.put(inputValue, new PairValidityAndErrorMessage(Boolean.TRUE));
              return filePath;
          }
          catch (IOException e)
          {
              System.out.println("Error while trying to download the file: "+ e.getMessage());
              this.lastDownloadErrorMessage=e.getMessage();
              
              /** add the input to the list of invalid invalid inputs **/
              String msg=(e.getMessage()==null)?null:e.getMessage().replaceAll("[\n\r]", " ");
              historyOfCalls.put(inputValue, new PairValidityAndErrorMessage(Boolean.FALSE, msg)); 
              return null;
          }
          catch (Exception e){
        	  	System.out.println("Error while converting JSON to XML "+ e.getMessage());
            this.lastDownloadErrorMessage=e.getMessage();
        	  	return null;
          }
          finally
          {
              try
              {
                  if(writer!=null) writer.close();
                  if(in!=null) in.close();
              }
              catch (IOException e)
              {
                  e.printStackTrace();
              }
          }
      }
      
  
    /*********************************/
    /** Read inputs from files **/
    /********************************/
    public static final ArrayList<String> readLineByLineTheInputs(String fileInputs) throws Exception{
		ArrayList<String> inputsList= new ArrayList<String>();
		BufferedReader br = new BufferedReader(new FileReader(fileInputs));
		String line;
		while ((line = br.readLine()) != null) {
			String input=line.trim();
 		    input=input.replaceAll("\\s+","+");
			inputsList.add(input);
		}
		br.close();
		return inputsList;
	}
     

    public static final ArrayList<String> readInputsFromInputsAndEntitiesFiles(String fileInputs, int column) throws Exception{
		ArrayList<String> inputsList= new ArrayList<String>();
		BufferedReader br = new BufferedReader(new FileReader(fileInputs));
		String line;
		while ((line = br.readLine()) != null) {
			String input=line.trim();
			String[] splits = input.split(Constants.separatorForInputsFiles);
			if(splits.length>=column) inputsList.add(splits[column-1].trim().replaceAll("\\s+","+"));
		}
		br.close();
		return inputsList;
	}

    /*********************************/
    /** Cache management **/
    /********************************/
  	public void storeCache() throws Exception{
  		if(historyOfCalls.keySet().size()==0) return;
  		
  		String filePath=Constants.getHistoryOfCallsFileForFunction(site, functionName);
  		FileWriter fOut = new FileWriter(filePath);
		String newLine = System.getProperty("line.separator");
		
		for(String input: historyOfCalls.keySet()){
			StringBuffer line=new StringBuffer();
			line.append(input);
			line.append(Constants.separatorForInputsFiles);
			
			PairValidityAndErrorMessage valMdg=historyOfCalls.get(input);
			Boolean wasValidAnswer=valMdg.validity;
			if(wasValidAnswer) line.append(Constants.VALID);
			else {
				line.append(Constants.INVALID);
				line.append(Constants.separatorForInputsFiles);
				line.append(valMdg.errorMessage);
			}
			fOut.write(line.toString()+newLine);
		}
		fOut.close();
  	}
    
  	public void loadCache() throws Exception{
  		historyOfCalls= new HashMap<String, PairValidityAndErrorMessage>();
  		
  		String filePath=Constants.getHistoryOfCallsFileForFunction(site, functionName);
  		
  		/** if no file with the history return **/
  		File  file= new File(filePath);
  		if(!file.exists()) {
  			System.out.println("The file with the hisory does not exit: "+filePath);
  			return;
  		}
  		
  		BufferedReader br = new BufferedReader(new FileReader(filePath));
		String line;
		while ((line = br.readLine()) != null) {
		   line=line.trim();
		   String[] splits = line.split(Constants.separatorForInputsFiles);
		   if(splits.length<2) continue;
		   String input=splits[0];
		   Boolean valid=(splits[1].equalsIgnoreCase(Constants.VALID))?Boolean.TRUE: Boolean.FALSE;
		   String msg=(splits.length>=3)?splits[2]:null;
		   historyOfCalls.put(input, new PairValidityAndErrorMessage(valid,msg));
		}
		br.close();
  	}
  	
  	public static final class PairValidityAndErrorMessage{
  		public Boolean validity;
  		public String errorMessage;
  		
  		public PairValidityAndErrorMessage(Boolean validity, String errorMessage){
  			this.validity=validity;
  			this.errorMessage=errorMessage;
  		}
  		
  		public PairValidityAndErrorMessage(Boolean validity)
  		{
  			this.validity=validity;
  			this.errorMessage=null;
  		}
  	}
  	
  	
    public static void main(String[] args) throws Exception
    {  
    		
    }
}
